package tech.hdis.framework.security.session.imp;

import com.google.gson.Gson;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
import tech.hdis.framework.security.properties.SecuritySessionProperties;
import tech.hdis.framework.security.session.entity.Session;
import tech.hdis.framework.security.session.interfaces.SessionService;

import java.util.Calendar;
import java.util.Date;
import java.util.Set;

/**
 * 基于redis实现的session管理
 *
 * @author 黄志文
 */
@Slf4j
@Component
public class RedisSessionServiceImpl implements SessionService {

    private Gson gson = new Gson();

    private ThreadLocal<Session> sessionThreadLocal = new ThreadLocal<>();

    private Integer refreshStepSize = 60;

    @Autowired
    protected SecuritySessionProperties securitySessionProperties;
    @Autowired
    protected RedisSessionClient redisSessionClient;

    /**
     * 新增或修改session；
     */
    @Override
    public void saveSession(Session session) {
        session.setLastAccessedTime(new Date());
        redisSessionClient.set(session.getToken(), gson.toJson(session), securitySessionProperties.getTimeout());
        sessionThreadLocal.set(session);
    }

    /**
     * 绑定当前线程session
     *
     * @param sessionId
     */
    @Override
    public void bindingSession(String sessionId) {
        String sessionJsonString = redisSessionClient.get(sessionId);
        if (!StringUtils.isEmpty(sessionJsonString)) {
            Session session = gson.fromJson(sessionJsonString, Session.class);
            sessionThreadLocal.set(session);
        } else {
            sessionThreadLocal.remove();
        }
    }

    /**
     * 刷新session，将最后访问时间与当前时间同步；
     */
    @Override
    public void refresh() {
        Session session = getSession();
        if (session == null) {
            return;
        }
        //避免高并发量，1分钟执行一次redis写入
        Calendar refreshStep = Calendar.getInstance();
        refreshStep.setTime(session.getLastAccessedTime());
        refreshStep.add(Calendar.SECOND, refreshStepSize);
        if (new Date().after(refreshStep.getTime())) {
            //执行写入
            redisSessionClient.del(session.getToken());
            session.setLastAccessedTime(new Date());
            redisSessionClient.set(session.getToken(), gson.toJson(session), securitySessionProperties.getTimeout());
            sessionThreadLocal.set(session);
        }
    }

    /**
     * 刷新session
     * 1：判断当前session是否失效
     * 2：处理单端单用户登录（一个‘用户账号’的‘手机端’只能有一个在线）
     *
     * @return 是否登录成功
     */
    @Override
    public Boolean flush() {
        return true;
        /*
        Session session = getSession();
        if (session == null) {
            return false;
        }
        //获得此username所有的session（username格式“mobile:username”）
        Object sobj = this.sessionRepository.findByIndexNameAndIndexValue(FindByIndexNameSessionRepository.PRINCIPAL_NAME_INDEX_NAME, (String) session.getAttribute(FindByIndexNameSessionRepository.PRINCIPAL_NAME_INDEX_NAME));
        //强制类型转化，普通转化无效
        Map<String, ExpiringSession> sMap = (Map<String, ExpiringSession>) sobj;
        //删除当前账户的所有session，只保留当前session，达到单端单用户登录
        for (String key : sMap.keySet()) {
            if (!tokenThreadLocal.get().equals(key)) {
                this.sessionRepository.delete(key);
            }
        }
        return true;
        */
    }

    /**
     * 登出，需要已登录状态
     */
    @Override
    public void logout() {
        Session session = getSession();
        if (session == null) {
            return;
        }
        redisSessionClient.del(session.getToken());
        sessionThreadLocal.remove();
    }

    /**
     * 获取当前Session
     */
    @Override
    public Session getSession() {
        Session session = sessionThreadLocal.get();
        return session;
    }

    /**
     * session是否失效
     */
    @Override
    public boolean isExpired() {
        return getSession() == null;
    }

    /**
     * 是否具有角色
     *
     * @param roles 角色字符串
     * @return 是否具有角色
     */
    @Override
    public Boolean hasRoles(String[] roles) {
        Session session = getSession();
        if (session == null) {
            return false;
        }
        Set<String> sessionRoles = session.getRoles();
        if (sessionRoles != null && roles != null) {
            if (roles.length != 0 && "".equals(roles[0])) {
                return true;
            } else {
                Boolean hasRoles = false;
                for (String role : roles) {
                    for (String sessionRole : sessionRoles) {
                        if (role.equals(sessionRole)) {
                            hasRoles = true;
                        }
                    }
                }
                return hasRoles;
            }
        }
        return true;
    }

    /**
     * 是否具有权限
     *
     * @param permissions 权限字符串
     * @return 是否具有权限
     */
    @Override
    public Boolean hasPermissions(String[] permissions) {
        Session session = getSession();
        if (session == null) {
            return false;
        }
        Set<String> sessionPermissions = session.getPermissions();
        if (sessionPermissions != null && permissions != null) {
            if (permissions.length != 0 && "".equals(permissions[0])) {
                return true;
            } else {
                Boolean hasPermissions = false;
                for (String permission : permissions) {
                    for (String sessionPermission : sessionPermissions) {
                        if (permission.equals(sessionPermission)) {
                            hasPermissions = true;
                        }
                    }
                }
                return hasPermissions;
            }
        }
        return true;
    }
}
